package ui;

import java.awt.BorderLayout;
import java.awt.FileDialog;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Properties;

import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLCapabilities;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JSlider;
import javax.swing.JSplitPane;
import javax.swing.SwingConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import data.Drawable;
import data.Edge;
import data.Node;
import data.Rings;
import data.Vector2;

import logic.CanvasHandler;
import logic.GraphGenerator;
import logic.Visualization;
import logic.GraphGenerator.Graph;


public class GUI extends JFrame {

	/**
	 * 
	 */
	private Properties properties;
	private static final long serialVersionUID = 1L;
	private Rectangle bounds;
	private GLCanvas canvas;
	private CanvasHandler canvasHandler;
	private JLabel help;
	private LinkedList<JComponent> componentList;
	private String helpText = "Drag the Mouse, to move the Camera. Use the mouse wheel to zoom. Double Click on a Node to make it the new Focus Node.";
	public enum NodeAction {INSERT, EDIT, REMOVE, INSERT_EDGE, NONE};
	private static NodeAction nodeAction;
	private static Node selectedNode = null;
	private static GUI _this = null;
	public GUI(){
		super();
		_this = this;
		componentList = new LinkedList<JComponent>();
		nodeAction = NodeAction.NONE;
		JPanel leftPanel = new JPanel();
		leftPanel.setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		c.fill = GridBagConstraints.HORIZONTAL;
		c.ipady = 5;
		c.weighty = 1;
		JPanel rightPanel = new JPanel();
		//this.setLayout(new FlowLayout());
		rightPanel.setLayout(new BorderLayout());
		JSplitPane splitPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightPanel);
		getContentPane().add(splitPanel);

		properties = new Properties();
		bounds = new Rectangle(0, 0, 800, 600);
		this.loadProperties();
		this.setBounds(bounds);
		GLCapabilities cap = new GLCapabilities();
		canvas = new GLCanvas(cap);
		LinkedList<Drawable>drawList = new LinkedList<Drawable>();
		drawList.add(new Rings());
		canvasHandler = new CanvasHandler(drawList);
		canvas.addGLEventListener(canvasHandler);
		canvas.addMouseMotionListener(canvasHandler);
		canvas.addMouseListener(canvasHandler);
		canvas.addMouseWheelListener(canvasHandler);
		canvas.addKeyListener(canvasHandler);
		canvas.setVisible(true);
		help = new JLabel(helpText);
		rightPanel.add(help, BorderLayout.NORTH);
		rightPanel.add(canvas);

		JLabel nodeLabel = new JLabel("NODE");
		c.gridy ++;
		c.fill = GridBagConstraints.CENTER;
		leftPanel.add(nodeLabel, c);
		c.fill = GridBagConstraints.HORIZONTAL;
		JButton insert = new JButton("Insert");
		insert.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				GUI.setNodeAction(NodeAction.INSERT);
			}
		});
		c.gridy ++;
		leftPanel.add(insert, c);
		componentList.add(insert);

		JButton edit = new JButton("Edit");
		edit.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				GUI.setNodeAction(NodeAction.EDIT);
			}
		});
		c.gridy ++;
		leftPanel.add(edit, c);
		componentList.add(edit);

		JButton remove = new JButton("Remove");
		remove.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				GUI.setNodeAction(NodeAction.REMOVE);
			}
		});
		c.gridy ++;
		leftPanel.add(remove, c);
		componentList.add(remove);

		JButton insert_edge = new JButton("Insert Edge");
		insert_edge.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				GUI.setNodeAction(NodeAction.INSERT_EDGE);
			}
		});
		c.gridy ++;
		leftPanel.add(insert_edge, c);
		componentList.add(insert_edge);

		c.gridy++;
		leftPanel.add(new JSeparator(SwingConstants.HORIZONTAL), c);


		JLabel manageLabel = new JLabel("MANAGE DATA");
		c.gridy ++;
		c.fill = GridBagConstraints.CENTER;
		leftPanel.add(manageLabel, c);
		c.fill = GridBagConstraints.HORIZONTAL;

		JButton load = new JButton("Load Graph");
		load.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				/*JFileChooser fc = new JFileChooser();
				int ret = fc.showOpenDialog(GUI.this);
				File file = fc.getSelectedFile();
				if(ret == JFileChooser.APPROVE_OPTION ){
					Graph graph = GraphGenerator.Graph.loadAbsolute(file.toString());
					canvasHandler.setDrawableList(generateDrawData(graph));
					new Visualization().start();
				}*/
				FileDialog fd = new FileDialog(GUI.this, "Load", FileDialog.LOAD);
				fd.setVisible(true);
				String file = fd.getFile();
				String dir = fd.getDirectory();
				if(file != null){
					Graph graph = GraphGenerator.Graph.loadAbsolute(dir+file);
					canvasHandler.setDrawableList(generateDrawData(graph));
					new Visualization().start();
				}
			}
		});
		c.gridy ++;
		leftPanel.add(load, c);
		componentList.add(load);
		JButton save = new JButton("Save Graph");
		save.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				FileDialog fd = new FileDialog(GUI.this, "Save", FileDialog.SAVE);
				fd.setVisible(true);
				String file = fd.getFile();
				String dir = fd.getDirectory();
				if(file != null){
					GraphGenerator.Graph.saveAbsolute(dir+file, Visualization.getGraphProcessing());
				}
			}
		});
		c.gridy ++;
		leftPanel.add(save, c);
		componentList.add(save);
		JButton generate = new JButton("Generate Random");
		generate.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				Graph graph = GraphGenerator.GenerateRandomGraph_constNodeSize(20, 1);
				canvasHandler.setDrawableList(generateDrawData(graph));
				new Visualization().start();
			}
		});
		c.gridy ++;
		leftPanel.add(generate, c);
		componentList.add(generate);

		c.gridy++;
		leftPanel.add(new JSeparator(SwingConstants.HORIZONTAL), c);

		JLabel viewLabel = new JLabel("CHANGE VIEW");
		c.gridy ++;
		c.fill = GridBagConstraints.CENTER;
		leftPanel.add(viewLabel, c);
		c.fill = GridBagConstraints.HORIZONTAL;

		JLabel ringScalingLabel = new JLabel("Scale Rings");
		c.gridy++;
		leftPanel.add(ringScalingLabel, c);
		JSlider ringScaling = new JSlider(1,100,(int)(Visualization.getRingScaling()*10));
		ringScaling.addChangeListener(new ChangeListener() {			
			@Override
			public void stateChanged(ChangeEvent e) {
				Visualization.setRingScaling(((JSlider)e.getSource()).getValue()/10.f);
				Visualization.setNodesInstantlyToNewPos();
			}
		});
		c.gridy ++;
		leftPanel.add(ringScaling, c);
		componentList.add(ringScaling);

		JLabel ringDisortLabel = new JLabel("Disort Rings");
		c.gridy++;
		leftPanel.add(ringDisortLabel, c);
		JSlider ringDisort = new JSlider(10,20,(int)Visualization.getRingDisort()*10);
		ringDisort.addChangeListener(new ChangeListener() {			
			@Override
			public void stateChanged(ChangeEvent e) {
				Visualization.setRingDisort(((JSlider)e.getSource()).getValue()/10.0f);
				Visualization.setNodesInstantlyToNewPos();
			}
		});
		c.gridy ++;
		leftPanel.add(ringDisort, c);
		componentList.add(ringDisort);

		JLabel animationSpeedLabel = new JLabel("Animation Time");
		c.gridy++;
		leftPanel.add(animationSpeedLabel, c);
		JSlider animationSpeed = new JSlider(0,100,(int)(Visualization.getAnimationTiming()/100000000L));
		animationSpeed.addChangeListener(new ChangeListener() {			
			@Override
			public void stateChanged(ChangeEvent e) {
				Visualization.setAnimationTiming((long)(((JSlider)e.getSource()).getValue()*100000000.0f));
			}
		});
		c.gridy ++;
		leftPanel.add(animationSpeed, c);
		componentList.add(animationSpeed);


		JCheckBox showNames = new JCheckBox("show Names");
		showNames.setSelected(CanvasHandler.isShowText());
		showNames.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				CanvasHandler.setShowText(((JCheckBox)e.getSource()).isSelected());
			}
		});
		c.gridy ++;
		leftPanel.add(showNames, c);
		componentList.add(showNames);

		JCheckBox showNoneTree = new JCheckBox("show None-Tree Edges");
		showNoneTree.setSelected(true);
		showNoneTree.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				Edge.setDrawNoneTreeEdges(((JCheckBox)e.getSource()).isSelected());
			}
		});
		c.gridy ++;
		leftPanel.add(showNoneTree, c);
		componentList.add(showNoneTree);

		this.addWindowListener(new WindowListener(){
			@Override
			public void windowActivated(WindowEvent e) {

			}
			@Override
			public void windowClosed(WindowEvent e) {
			}
			@Override
			public void windowClosing(WindowEvent e) {
				GUI.this.saveProperties();
				System.exit(NORMAL);
			}
			@Override
			public void windowDeactivated(WindowEvent e) {

			}
			@Override
			public void windowDeiconified(WindowEvent e) {

			}
			@Override
			public void windowIconified(WindowEvent e) {

			}
			@Override
			public void windowOpened(WindowEvent e) {

			}
		});
		//canvas.setSize(300,300);
	}
	public static void main(String [] args){
		GUI gui = new GUI();
		gui.setVisible(true);
		while(true){
			gui.update();
		}
	}
	private void saveProperties(){
		this.properties.setProperty("last_used", Long.toString(System.currentTimeMillis()));
		this.properties.setProperty("window_pos_x", Integer.toString(this.getBounds().x));
		this.properties.setProperty("window_pos_y", Integer.toString(this.getBounds().y));
		this.properties.setProperty("window_width", Integer.toString(this.getBounds().width));
		this.properties.setProperty("window_height", Integer.toString(this.getBounds().height));
		try {
			this.properties.store(new FileOutputStream("config.ini"), "Configuration of AEODGWRL");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	private void loadProperties(){
		try {
			this.properties.load(new FileInputStream("config.ini"));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		long dtMili = Long.parseLong(this.properties.getProperty("last_used"));
		Date dt = new Date(dtMili);
		Calendar cal = Calendar.getInstance();
		cal.setTime(dt);
		int sec = cal.get(Calendar.SECOND);
		int min = cal.get(Calendar.MINUTE);		
		int hour = cal.get(Calendar.HOUR_OF_DAY);
		int day = cal.get(Calendar.DAY_OF_MONTH);
		int month = cal.get(Calendar.MONTH);
		int year = cal.get(Calendar.YEAR);
		System.out.println("Last used: "+hour+":"+min+":"+sec+" "+day+"."+month+"."+year);
		bounds.x = Integer.parseInt(this.properties.getProperty("window_pos_x"));
		bounds.y = Integer.parseInt(this.properties.getProperty("window_pos_y"));
		bounds.width = Integer.parseInt(this.properties.getProperty("window_width"));
		bounds.height = Integer.parseInt(this.properties.getProperty("window_height"));
	}
	public void update(){
		canvas.update(null);
	}
	public void setAllComponentsEnabled(boolean enabled){
		Iterator<JComponent>iterator = componentList.iterator();
		while(iterator.hasNext())
			iterator.next().setEnabled(enabled);
	}
	private LinkedList<Drawable> generateDrawData(Graph graph){
		LinkedList<Node> nodeList = (LinkedList<Node>) graph.getNodeList();
		HashMap<Integer, LinkedList<Node>> neighborList = graph.getNeighbors();

		//LinkedList<Node> nodeList = new LinkedList<Node>();
		LinkedList<Drawable> drawableList = new LinkedList<Drawable>();

		//draw rings
		drawableList.add(new Rings() );

		//draw edges
		Iterator<Node> nodeIt = nodeList.iterator();
		while (nodeIt.hasNext()){
			Node node = nodeIt.next();
			Iterator<Node> nghbr = neighborList.get(node.getID()).iterator();
			while (nghbr.hasNext())
			{
				Node nghbrNode = nghbr.next();
				drawableList.add(new Edge(node, nghbrNode));
			}
		}	

		//draw nodes
		Visualization.setFocusNode(nodeList.getFirst());
		Visualization.getGraphProcessing().generateTree(Visualization.getFocusNode());
		//DepthFirstIterator nodeItDF = Visualization.getGraphProcessing().iteratorDepthFirst();
		Iterator<Node>nodeItDF = nodeList.iterator();
		//rest of the nodes
		while (nodeItDF.hasNext()){
			//for(int i = 0; i < 1000; i++){
			//Node node = new Node(i, 10, "Node"+i);
			Node node = nodeItDF.next();
			if(node.getNeighbors().size() == 0)
				continue;
			node.setAngle((float)(Math.random()*Math.PI*2));
			node.setOldAngle((float)(Math.random()*Math.PI*2));
			node.setRing((int)(Math.random()*10));
			node.setOldRing((int)(Math.random()*10));

			//nodeList.add(node);
			//nodeList.set(node.getID(), node);
			drawableList.add(node);
		}
		/*DepthFirstIterator bfi = visualization.getGraphProcessing().iteratorDepthFirst();
		System.out.println("================depth first search===============");
		while(bfi.hasNext()){
			System.out.println(bfi.next().toString());
			//bfi.next();
		}*/
		return drawableList;
	}
	public static void setNodeAction(NodeAction na){
		nodeAction = na;
		selectedNode = null;
		_this.canvas.requestFocus();
		switch (nodeAction){
		case NONE:
			_this.setAllComponentsEnabled(true);
			_this.help.setText(_this.helpText);
			break;
		case INSERT:
			if(Visualization.getFocusNode() == null){
				Node node = new Node(Node.maxID+1, 1.0f, "new node", "default.png");
				new NodeAttributeDialog(_this, node);
				Visualization.setFocusNode(node);
				setNodeAction(NodeAction.NONE);
				node.init(null);
				_this.canvasHandler.addDrawable(node);
			}else{
				_this.setAllComponentsEnabled(false);
				_this.help.setText("Double Click on the node you want to be the father of the new node. Press ESC to cancel.");
			}break;
		case REMOVE:
			_this.setAllComponentsEnabled(false);
			_this.help.setText("Double Click on the node you want to remove. Press ESC to cancel.");
			break;
		case INSERT_EDGE:
			_this.setAllComponentsEnabled(false);
			_this.help.setText("Double Click on the first of two nodes. Press ESC to cancel.");
			break;
		case EDIT:
			_this.setAllComponentsEnabled(false);
			_this.help.setText("Double Click on a node to edit it. Press ESC to cancel.");
			break;
		}
	}
	public static void selectNodeAndPerformNodeAction(Vector2 pos){
		Node node = Visualization.getNodeAt(pos);
		if(node == null)
			return;
		switch (nodeAction){
		case NONE:
			if(node != Visualization.getFocusNode()){
				Visualization.setFocusNode(node);
				new Visualization().start();
			}
			break;
		case INSERT:
			Node child = new Node(Node.maxID+1, 1.0f, "new node", "default.png");
			child.setPosition(node.getPosition().x, node.getPosition().y);
			child.setAngle(node.getAngle());
			child.setRing(node.getRing());
			new NodeAttributeDialog(_this, child);
			Visualization.getGraphProcessing().addChild(child, node);
			new Visualization().start();
			setNodeAction(NodeAction.NONE);
			child.init(null);
			_this.canvasHandler.addDrawable(child);
			_this.canvasHandler.addDrawableBeforeFirst(new Edge(child, node));
			break;
		case REMOVE:
			if(node == Visualization.getFocusNode()){
				Visualization.setFocusNode(null);
				Visualization.getGraphProcessing().removeSubtree(node);
				setNodeAction(NodeAction.NONE);
			}else{
				Visualization.getGraphProcessing().removeSubtree(node);
				new Visualization().start();
				setNodeAction(NodeAction.NONE);
			}
			//_this.canvasHandler.removeDrawable(node);
			break;
		case INSERT_EDGE:
			if(selectedNode == null){
				selectedNode = node;
				_this.help.setText("Double Click on the second node. Press ESC to cancel.");
			}else{
				if(selectedNode == node)
					return;
				Visualization.getGraphProcessing().addEdge(node, selectedNode);
				new Visualization().start();
				_this.canvasHandler.addDrawableBeforeFirst(new Edge(selectedNode, node));
				setNodeAction(NodeAction.NONE);
				selectedNode = null;
			}
			break;
		case EDIT:
			setNodeAction(NodeAction.NONE);
			new NodeAttributeDialog(GUI._this, node);
			break;
		}
	}
}
